package aquerius

import annotation.tailrec

/**
 * @author: Yardena
 * @date: Jan 3, 2010 4:43:15 PM
 */

object Basic {

  trait Domain[+P<:Pattern] {
    def extract[U>:P<:Pattern](pattern: U):Option[U#Value]
    def domain[U>:P<:Pattern](pattern: U):Domain[U]
  }

  trait Pattern {
    type This = this.type
    type Value
    def default:Value
    def unapply(domain: Domain[This]):Option[Value] = domain.extract(this)
    def /:(domain: Domain[This]) = unapply(domain).getOrElse(default)
    def map(f:()=>Value):Domain[This] = new Mapping(f)
    private class Mapping(f:()=>Value) extends Domain[This] {
      def extract[U>:This<:Pattern](pattern: U) = if (Pattern.this == pattern) Some(f()) else None
      def domain[U>:This<:Pattern](pattern: U) = if (Pattern.this == pattern) this else null
    }
  }

  sealed class NilRecord extends Domain[Pattern] {
    def extract[P>:Pattern<:Pattern](pattern: P) = None
    def domain[P>:Pattern<:Pattern](pattern: P) = null
    def ::[P<:Pattern](d: Domain[P]) = new Record(d,this)
  }
  object NilRecord extends NilRecord

  sealed class Record[P1<:Pattern,P2<:Pattern](d1:Domain[P1], d2:Domain[P2])
          extends Domain[P1 with P2]
  {
    type P = P1 with P2
    def ::[Q<:Pattern](d: Domain[Q]):Domain[P with Q] = new Record[Q,P](d,this)
    def extract[U>:P<:Pattern](pattern: U) = d1.extract(pattern).
                                             orElse(d2.extract(pattern)).
                                             asInstanceOf[Option[U#Value]]
    def domain[U>:P<:Pattern](pattern: U):Domain[U] = {
      var d = d1.domain(pattern)
      if (d == null) d = d2.domain(pattern)
      d.asInstanceOf[Domain[U]]
    }
  }

  sealed trait HList {
    type This <: HList
    type Head
    type Tail <: HList
    def get(i:Int):Any = nth(this,i)
    def ::[V](v : V): HCons[V,This]
  }

  sealed trait HEmpty extends HList {
    type This = HEmpty
    type Head = Nothing
    type Tail = HEmpty
    def ::[V](v : V) = HCons(v, this)
  }

  object HEmpty extends HEmpty

  sealed case class HCons[H,T <: HList](val head : H, val tail : T) extends HList {
    type This = HCons[H,T]
    type Head = H
    type Tail = T
    def ::[V](v : V) = HCons(v, this)
  }

  @tailrec private def nth(r:HList, n:Int):Any = {
    r match { case HEmpty => null; case c:HCons[_,_] => if (n==0) c.head else nth(c.tail, n-1) }
  }

  type ::[H, T <: HList] = HCons[H, T]

  sealed trait Field[+V] { type Value = V; def value: Value }
  case class Property[T](override val value: T) extends Field[T]

  sealed trait Header {
    type This <: Header
    type Head
    type Tail <: Header
    def ::[F <: Field[_]](f : F): ConsHeader[F,This]

    type Flat <: HList
    def flat: Flat
  }

  sealed trait End extends Header {
    type This = End
    type Head = Nothing
    type Tail = End
    def ::[F <: Field[_]](f : F) = new ConsHeader[F,End](f, this)

    type Flat = HEmpty
    def flat = HEmpty
  }

  object End extends End

  sealed class ConsHeader[H <: Field[_], T <: Header](head : H, tail : T) extends Header {
    type This = ConsHeader[H, T]
    type Head = H
    type Tail = T
    def ::[F <: Field[_]](f : F) = new ConsHeader(f, this)

    type Flat = HCons[Head#Value,Tail#Flat]
    def flat: Flat = HCons(head.value,tail.flat)
  }

}